package com.nts.visionworks.core.hibernate.transform;

import java.beans.Introspector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import org.hibernate.transform.ResultTransformer;

/**
 * 쿼리 결과에 대해서 애플리케이션 종단에 전달된 쿼리 결과 목록으로 변형하기 위한 전략을 구현한다.
 * 이 구현은 데이터베이스의 사용자 정의 프로시저나 사용자 정의 함수에 의해 실행된 쿼리 결과가 매핑되기 원하는 클래스의 필드 타입과 불일치하게 되는 위해 작성되었다.
 * @author Devcken
 * @param <T> 쿼리 결과가 매핑되기 원하는 클래스의 제네릭을 정의한다.
 */
public class AutoCastableResultTransformer<T> implements ResultTransformer
{
	private static final long serialVersionUID = -8781670903288702605L;
	
	/**
	 * 쿼리 결과가 매핑되기 원하는 클래스를 나타낸다.
	 */
	private Class<T> _class;
	
	public AutoCastableResultTransformer(Class<T> cls)
	{
		super();

		this._class = cls;
	}

	/**
	 * 쿼리 결과 중 한 개의 레코드에 대해서 원하는 형태로 변형한다.
	 * @param tuple 쿼리 결과 중 한 개의 레코드가 전달된다.
	 * @param aliases 전달된 레코드의 각 요소들의 별칭 목록이 전달된다.
	 * @return 정의된 규칙에 의해 변형된 레코드가 반환된다. 이 구현에서는 레코드의 요소를 {@link java.lang.String}으로 변환한 다음에 클래스의 필드 타입에 맞게 변환하여 설정된 인스턴스가 반환된다.
	 */
	@Override
	public Object transformTuple(Object[] tuple, String[] aliases)
	{
		try
		{
			// 반환될 클래스의 인스턴스를 생성한다.
			Object target = this._class.newInstance();
			
			for (int index = 0; index < aliases.length; index++)
			{
				String alias = aliases[index];
				
				// 필드의 타입을 알아내기 위해 쿼리 별칭에 들어맞는 필드를 가져온다.
				Field field = this._class.getDeclaredField(Introspector.decapitalize(alias));
				
				Class<?> valueType = field.getType();
				
				// 필드에 대한 setter를 가져온다.
				Method method = this._class.getDeclaredMethod("set" + alias, new Class<?>[] { valueType });
				
				// 필드 타입에 대한 생성자를 가져온다.
				Constructor<?> constructor = valueType.getDeclaredConstructor(String.class);
				
				// 필드 타입과 별칭에 맞는 요소 값으로 필드의 인스턴스를 생성하여 레코드에 대한 인스턴스에 설정한다.
				method.invoke(target, (Object)constructor.newInstance(tuple[index].toString()));
			}
			
			return target;
		}
		catch (Exception error)
		{
			return null;
		}
	}

	/**
	 * 전체 쿼리 결과에 대해서 변환된 목록을 전달한다.(전체 쿼리 목록을 여기서 변형할 수도 있다.)
	 * @param collection 쿼리 결과에 대해서 transformTuple 메서드에 의해 변환된 목록이 전달된다.
	 * @return 전체 쿼리 결과에 대해 변환된 목록을 반환한다.
	 */
	@SuppressWarnings("rawtypes")
	@Override
	public List transformList(List collection)
	{
		// 이 구현에서는 전체 목록에 대해서 어떤 변형도 하지 않고 전달한다.
		return collection;
	}
}